<script setup lang="ts">
import {
    AdditiveBlending,
    AmbientLight,
    BackSide,
    BoxGeometry,
    BufferGeometry,
    CatmullRomCurve3,
    DoubleSide,
    Float32BufferAttribute,
    Fog,
    MathUtils,
    Mesh,
    MeshBasicMaterial,
    MeshPhongMaterial,
    NormalBufferAttributes,
    Object3DEventMap,
    PerspectiveCamera,
    PlaneGeometry,
    PointLight,
    Points,
    PointsMaterial,
    Scene,
    SphereGeometry,
    TextureLoader,
    Vector3,
    WebGLRenderer
} from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import _ from 'lodash'
import { ref, onMounted } from 'vue'
const threeContainerRef = ref<HTMLDivElement>()
let scene: Scene,
    renderer: WebGLRenderer,
    camera: PerspectiveCamera,
    width: number,
    height: number,
    // 星空盒的深度
    depth: number = 1000,
    // 相机z轴的位置
    zAxis: number,
    // 轨道控制器
    controls: OrbitControls,
    // 星球
    sphere: Mesh,
    // 点的参数0
    pointsParam: Array<number & any>,
    // 点
    points: Array<PointsMaterial> = [],
    // 点的初始位置
    pointsZPosition: number,
    // 点1 在z轴的位置
    pointFirstProgressZPosition: number,
    // 点2 在z轴的位置
    pointSecondProgressZPosition: number,
    pointFirstObj: Points<
        BufferGeometry<NormalBufferAttributes>,
        PointsMaterial
    >[] = [],
    pointSecondObj: Points<
        BufferGeometry<NormalBufferAttributes>,
        PointsMaterial
    >[] = [],
    // 星云信息
    nebulaFirstInfo: Mesh<PlaneGeometry, MeshBasicMaterial, Object3DEventMap>,
    nebulaSecondInfo: Mesh<PlaneGeometry, MeshBasicMaterial, Object3DEventMap>,
    renderFirstNebulaFunc: Function,
    renderSecondNebulaFunc: Function

onMounted(() => {
    initScene()
    initSceneBackgound()
    heavenlyBody()
    initLight()
    initCamera()
    pointsZPosition = -zAxis - depth / 2
    pointFirstProgressZPosition = pointsZPosition
    pointSecondProgressZPosition = pointsZPosition * 2
    pointFirstObj = initStars(pointsZPosition)
    pointSecondObj = initStars(pointSecondProgressZPosition)
    nebulaFirstInfo = renderNebula(400, 200)
    nebulaSecondInfo = renderNebula(200, 100)
    renderFirstNebulaFunc = renderNebulaProgress(
        nebulaFirstInfo,
        [
            new Vector3(-width / 10, 0, -depth / 2),
            new Vector3(-width / 4, height / 8, zAxis)
        ],
        0.0002
    )
    renderSecondNebulaFunc = renderNebulaProgress(
        nebulaSecondInfo,
        [new Vector3(width / 10, 0, -depth / 2), new Vector3(width / 4, 0, zAxis)],
        0.0008
    )
    initRenderer()
    initControls()
    animate()
    window.addEventListener('resize', animate)
})
/**
 * 初始化场景
 */
const initScene = () => {
    width = window.innerWidth || 0
    height = window.innerHeight || 0
    scene = new Scene()
    scene.fog = new Fog(0x000000, 1, 10000)
}
/**
 * 初始化背景
 */
const initSceneBackgound = () => {
    new TextureLoader().load(
        new URL('/@/assets/images/sky.png', import.meta.url).href,
        (texture: any) => {
            // 创建立方体
            const cube = new Mesh(
                new BoxGeometry(width, height, depth),
                new MeshBasicMaterial({ map: texture, side: BackSide })
            )
            scene.add(cube)
        }
    )
}

/**
 * 初始化相机
 */
const initCamera = () => {
    // 视野夹角
    const fov = 15
    // 计算相机距离物体的距离
    const distance = width / 2 / Math.tan(Math.PI / 11.5)
    // 相机z轴的位置： 相机距离物体的位置 - 立方体的深度 / 2
    zAxis = Math.floor(distance - depth / 2)
    // 创建相机
    camera = new PerspectiveCamera(fov, width / height, 1, 30000)
    camera.position.set(0, 0, zAxis)
    camera.lookAt(scene.position)
    scene.add(camera)
}
/**
 * 初始化星球
 */
const heavenlyBody = () => {
    sphere = new Mesh(
        new SphereGeometry(50, 64, 32),
        new MeshPhongMaterial({
            color: 0xffffff,
            map: new TextureLoader().load(
                new URL('/@/assets/images/earth_bg.png', import.meta.url).href
            )
        })
    )
    sphere.position.set(-400, 200, -200)
    scene.add(sphere)
}
/**
 * 星球自转
 */
const heavenlyBodyRotate = () => {
    sphere.rotation.y += 0.01
}
/**
 * 初始化场景星星的效果
 */
const initStars = (zP: number) => {
    const geometry = new BufferGeometry()
    const pointGeometry = []
    // 随机生成星星的顶点
    // 随机生成1000个星星的顶点信息，每个星星由3个顶点组成，每个顶点包含x,y,z三个坐标值
    const vertices = []
    // 创建纹理
    const textureLoader = new TextureLoader()
    const starflake1 = textureLoader.load(
        new URL('/@/assets/images/starflake1.png', import.meta.url).href
    )
    const starflake2 = textureLoader.load(
        new URL('/@/assets/images/starflake2.png', import.meta.url).href
    )
    // 点的参数
    pointsParam = [
        [[0.6, 100, 0.75], starflake1, 50],
        [[0, 0, 1], starflake2, 20]
    ]
    for (let i = 0; i < 1500; i++) {
        const x = MathUtils.randFloatSpread(width)
        const y = _.random(height)
        const z = _.random(-depth / 2, zAxis)
        vertices.push(x, y, z)
    }
    geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3))
    for (let i = 0; i < pointsParam.length; i++) {
        const color = pointsParam[i][0]
        const texture = pointsParam[i][1]
        const size = pointsParam[i][2]
        points[i] = new PointsMaterial({
            size,
            map: texture,
            blending: AdditiveBlending,
            transparent: true,
            depthTest: true
        })
        points[i].color.setHSL(color[0], color[1], color[2])
        const point = new Points(geometry, points[i])
        point.rotation.set(
            Math.random() * 0.2 - 0.15,
            Math.random() * 0.2 - 0.15,
            Math.random() * 0.2 - 0.15
        )
        point.position.setZ(zP)
        pointGeometry.push(point)
        scene.add(point)
    }
    return pointGeometry
}
/**
 * 初始化光源
 */
const initLight = () => {
    const ambientLight = new AmbientLight(0xffffff, 1)
    scene.add(ambientLight)
    const pointLight = new PointLight(0x0655df, 5, 0, 0)
    pointLight.position.set(0, 100, -200)
    scene.add(pointLight)
}
/**
 * 星星的运动
 */
const renderStatMove = () => {
    // 星星颜色交替变化
    const time = Date.now() * 0.00005
    for (let i = 0; i < pointsParam.length; i++) {
        const color = pointsParam[i][0]
        const h = ((360 * (color[0] + time)) % 360) / 360
        points[i].color.setHSL(h, color[1], parseFloat(h.toFixed(2)))
    }
    // 星星的运动
    pointFirstProgressZPosition += 1
    if (pointFirstProgressZPosition >= zAxis + depth / 2) {
        pointFirstProgressZPosition = pointsZPosition
    } else {
        pointFirstObj.forEach(item => {
            item.position.setZ(pointFirstProgressZPosition)
        })
    }
    pointSecondProgressZPosition += 1
    if (pointSecondProgressZPosition >= zAxis + depth / 2) {
        pointSecondProgressZPosition = pointsZPosition
    } else {
        pointSecondObj.forEach(item => {
            item.position.setZ(pointSecondProgressZPosition)
        })
    }
}
/**
 * 星云的效果
 */
const renderNebula = (
    width: number,
    height: number
): Mesh<PlaneGeometry, MeshBasicMaterial, Object3DEventMap> => {
    const nebula = new Mesh(
        new PlaneGeometry(width, height),
        new MeshBasicMaterial({
            map: new TextureLoader().load(
                new URL('//@/assets/images/cloud.png', import.meta.url).href
            ),
            blending: AdditiveBlending,
            transparent: true,
            depthTest: false,
            side: DoubleSide
        })
    )
    scene.add(nebula)
    return nebula
}
/**
 * 星云的运动
 */
const renderNebulaProgress = (
    nebula: Mesh<PlaneGeometry, MeshBasicMaterial, Object3DEventMap>,
    route: Vector3[] | undefined,
    speed: number
) => {
    let nebulaProgress = 0
    // 创建运动的轨迹
    const curve = new CatmullRomCurve3(route)
    return () => {
        if (nebulaProgress <= 1) {
            nebulaProgress += speed
            const point = curve.getPointAt(nebulaProgress)
            if (point && point.x) {
                nebula.position.set(point.x, point.y, point.z)
            }
        } else {
            nebulaProgress = 0
        }
    }
}
/**
 * 初始渲染器
 */
const initRenderer = () => {
    renderer = new WebGLRenderer()
    threeContainerRef.value?.appendChild(renderer.domElement)
}
/**
 * 初始化轨道控制器
 */
const initControls = () => {
    controls = new OrbitControls(camera, renderer.domElement)
    controls.enabled = true
    controls.update()
}
/**
 * 动画
 */
const animate = () => {
    width = window.innerWidth || 0
    height = window.innerHeight || 0
    heavenlyBodyRotate()
    renderStatMove()
    requestAnimationFrame(animate)
    renderer.setSize(width, height)
    renderFirstNebulaFunc()
    renderSecondNebulaFunc()
    renderer.render(scene, camera)
}
</script>
<template>
    <div ref="threeContainerRef" class="w-[100vw] h-[100vh] overflow-hidden"></div>
    <div class="login"></div>
</template>
<style lang="scss" scoped></style>
